//
//  LayerActionViewController.m
//  CoreAnimation
//
//  Created by 张国文 on 16/10/25.
//  Copyright © 2016年 hangzhouyijiyin. All rights reserved.
//

#import "LayerActionViewController.h"

@interface LayerActionViewController ()

@property (nonatomic, strong) UIView *backgroundView;

@property (nonatomic, strong) UIView *rectView;

@property (nonatomic, strong) CALayer *rectLayer;

@end

@implementation LayerActionViewController

/**
 *  我们可以确定，当属性在动画块之外发生改变，UIView直接通过返回nil来禁用隐式动画。
 
 UIView关联的图层禁用了隐式动画，对这种图层做动画的唯一办法就是使用UIView的动画函数（而不是依赖CATransaction），或者继承UIView，并覆盖-actionForLayer:forKey:方法，或者直接创建一个显式动画（具体细节在一下章会有详细的讲解）。
 对于单独存在的图层，我们可以通过实现图层的-actionForLayer:forKey:委托方法，或者提供一个actions字典来控制隐式动画。
 */

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    self.view.backgroundColor = [UIColor whiteColor];
    
    
    
    self.backgroundView = [[UIView alloc] initWithFrame:self.view.frame];
    self.backgroundView.backgroundColor = [[UIColor grayColor] colorWithAlphaComponent:0.5];
    [self.view addSubview:_backgroundView];
    
    // 视图
    self.rectView = [[UIView alloc] init];
    self.rectView.frame = CGRectMake(50, 50, 100, 100);
    self.rectView.backgroundColor = [UIColor redColor];
    [self.backgroundView addSubview:_rectView];
    
    self.rectLayer = [[CALayer alloc] init];
    self.rectLayer.frame = CGRectMake(50, 200, 100, 100);
    self.rectLayer.backgroundColor = [UIColor yellowColor].CGColor;
    [self.backgroundView.layer addSublayer:_rectLayer];
    
    NSLog(@"rectLayer的呈现图层%@", self.rectLayer.presentationLayer);
    
    UIButton *button1 = [UIButton buttonWithType:UIButtonTypeCustom];
    button1.frame = CGRectMake(50, 400, 300, 30);
    [button1 setTitle:@"CATransaction改变视图的关联图层" forState:UIControlStateNormal];
    [button1 setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
    [button1 addTarget:self action:@selector(button1:) forControlEvents:UIControlEventTouchUpInside];
    [self.backgroundView addSubview:button1];

    UIButton *button2 = [UIButton buttonWithType:UIButtonTypeCustom];
    button2.frame = CGRectMake(50, 450, 300, 30);
    [button2 setTitle:@"UIView动画块检测actionForLayer" forState:UIControlStateNormal];
    [button2 setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
    [button2 addTarget:self action:@selector(showTheReasonOfForbitCATransaction) forControlEvents:UIControlEventTouchUpInside];
    [self.backgroundView addSubview:button2];
    
    UIButton *button3 = [UIButton buttonWithType:UIButtonTypeCustom];
    button3.frame = CGRectMake(0, 500, 375, 30);
    [button3 setTitle:@"运用CATransition相应CAAction做过渡动画" forState:UIControlStateNormal];
    [button3 setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
    [button3 addTarget:self action:@selector(button3:) forControlEvents:UIControlEventTouchUpInside];
    [self.backgroundView addSubview:button3];
}

- (void)button1:(UIButton *)button
{
    // 运用CATransaction 改变视图的关联图层，视图颜色会瞬间改变，因为视图的关联图层禁用CATransaction
    [CATransaction begin];
    [CATransaction setAnimationDuration:3.0];
    CGFloat red = arc4random() % 256 / 255.0;
    CGFloat blue = arc4random() % 256 / 255.0;
    CGFloat green = arc4random() % 256 / 255.0;
    self.rectView.layer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1].CGColor;
    [CATransaction commit];
}

// 查看UIView的关联图层禁用CATransaction的原因
- (void)showTheReasonOfForbitCATransaction
{
    /**
     *  试想一下，如果UIView的属性都有动画特性的话，那么无论在什么时候修改它，我们都应该能注意到的。所以，如果说UIKit建立在Core Animation（默认对所有东西都做动画）之上，那么隐式动画是如何被UIKit禁用掉呢？
     
     我们知道Core Animation通常对CALayer的所有属性（可动画的属性）做动画，但是UIView把它关联的图层的这个特性关闭了。为了更好说明这一点，我们需要知道隐式动画是如何实现的。
     
     ⚠️⚠️⚠️CALayer属性动画的实质：
     我们把改变属性时CALayer自动应用的动画称作行为，当CALayer的属性被修改时候，它会调用-actionForKey:方法，传递属性的名称。剩下的操作都在CALayer的头文件中有详细的说明，实质上是如下几步：
     
    1、 图层首先检测它是否有委托，并且是否实现CALayerDelegate协议指定的-actionForLayer:forKey方法。如果有，直接调用并返回结果。
    2、 如果没有委托，或者委托没有实现-actionForLayer:forKey方法，图层接着检查包含属性名称对应行为映射的actions字典。
    3、如果actions字典没有包含对应的属性，那么图层接着在它的style字典接着搜索属性名。
    4、最后，如果在style里面也找不到对应的行为，那么图层将会直接调用定义了每个属性的标准行为的-defaultActionForKey:方法。
     所以一轮完整的搜索结束之后，-actionForKey:要么返回空（这种情况下将不会有动画发生），要么是CAAction协议对应的对象，最后CALayer拿这个结果去对先前和当前的值做动画。
     
     于是这就解释了UIKit是如何禁用隐式动画的：每个UIView对它关联的图层都扮演了一个委托，并且提供了-actionForLayer:forKey的实现方法。当不在一个动画块的实现中，UIView对所有图层行为返回nil，但是在动画block范围之内，它就返回了一个非空值。
     */
    
    NSLog(@"动画块之外%@", [self.rectView actionForLayer:self.rectView.layer forKey:@"backgroundColor"]);
    [UIView beginAnimations:@"" context:nil];
     NSLog(@"动画块里面%@", [self.rectView actionForLayer:self.rectView.layer forKey:@"backgroundColor"]);
    [UIView commitAnimations];
    
    /**
     *  运行结果
     CoreAnimation[23978:603725] 动画块之外<null>
     2016-10-25 14:04:57.250 CoreAnimation[23978:603725] 动画块里面<CABasicAnimation: 0x7f94c3706930>
     */
    
#pragma mark - 于是我们可以预言，当属性在动画块之外发生改变，UIView直接通过返回nil来禁用隐式动画。
}

- (void)button3:(UIButton *)button
{
     CATransition *transition = [CATransition animation];
    transition.type = kCATransitionPush;
    transition.subtype = kCATransitionFromLeft;
    [transition setDuration:0.5];
    
    self.rectLayer.actions = @{@"backgroundColor":transition};
    CGFloat red = arc4random() % 256 / 255.0;
    CGFloat blue = arc4random() % 256 / 255.0;
    CGFloat green = arc4random() % 256 / 255.0;
    self.rectLayer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1].CGColor;
    
    NSLog(@"依赖图层%@", [self.rectLayer modelLayer]);  // 就是self
     NSLog(@"rectLayer的呈现图层%@", [self.rectLayer presentationLayer]); // 就是rectLayer的呈现图层(就是展示于用户视觉效果的图层)
    
}

@end
